home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / Miro_Downloader.exe / storedatabase.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2007-11-12  |  40.3 KB  |  1,415 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. '''This module does the reading/writing of our database to/from disk.  It
  5. works with the schema module to validate the data that we read/write and with
  6. the upgradedatabase module to upgrade old database storages.
  7.  
  8. We avoid ever writing a DDB class to disk.  This allows us to change our
  9. classes without concern to how it will affect old databases.  For instance, we
  10. can delete classes and not have to worry about users with old databases that
  11. reference those classes.  Instead of class names, we write a string that
  12. represents the classes ("feed" instead of feed.Feed).  If we decide to delete
  13. the feed class, the upgrade code can handle upgrading old feed objects.
  14.  
  15. To achieve the above, before we save the DDBObjects to disk, we convert them
  16. to SavableObjects.  A SavableObject is a really simple storage container that
  17. remembers the class it was saved from and selected attributes of the object
  18. (one for each item in the object\'s schema).  When we restore DDBObjects, we
  19. need to convert the other way.
  20.  
  21. Right now we implement the conversion/unconversion using 2 classes
  22. (SavableConverter and SavableUnconverter) that share a base classes
  23. (ConverterBase).  Converter base handles walking the object tree, which is
  24. most of the actual conversion.  The SavableConverter and SavableUnconverter
  25. override some methods which are specific to the conversion/unconversion
  26. process.
  27.  
  28. '''
  29. import cPickle
  30. import os
  31. import traceback
  32. import shutil
  33. import config
  34. import database
  35. import databaseupgrade
  36. import prefs
  37. import util
  38. import schema as schema_mod
  39. import eventloop
  40. import app
  41. import bsddb.db as bsddb
  42. import dialogs
  43. import logging
  44. from zipfile import ZipFile
  45. import tempfile
  46. from random import randrange, seed
  47. import os.path as os
  48.  
  49. try:
  50.     from pysqlite2 import dbapi2 as sql
  51. except ImportError:
  52.     import sqlite3 as sql
  53.  
  54. from gtcache import gettext as _
  55. from clock import clock
  56. FILEMAGIC = 'Democracy Database V1'
  57. skipOnRestore = False
  58. skipUpgrade = False
  59.  
  60. class DatabaseError(Exception):
  61.     pass
  62.  
  63.  
  64. class BadFileFormatError(DatabaseError):
  65.     pass
  66.  
  67.  
  68. class NotImplementError(DatabaseError):
  69.     pass
  70.  
  71.  
  72. class _BootStrapClass:
  73.     pass
  74.  
  75.  
  76. class SavableObject:
  77.     """Object that can be safely pickled and saved to disk.  
  78.  
  79.     Member variables:
  80.  
  81.     classString -- specifies the class this object was converted from.  
  82.     savedData -- dict that stores the data we've saved.
  83.     
  84.     The SavableObject class is guarenteed to never change.  This means we can
  85.     always safely unpickle them.
  86.     """
  87.     
  88.     def __init__(self, classString):
  89.         self.classString = classString
  90.         self.savedData = { }
  91.  
  92.     
  93.     def __str__(self):
  94.         return '<SavableObject: %s>' % self.classString
  95.  
  96.  
  97.  
  98. class ConverterBase(object):
  99.     '''Base class for SavableConverter and SavableUnconverter.  It handles the
  100.     common tasks relating to converting the database to/from SavableObjects.
  101.     This include stuff like walking the object hierarchy, handling circular
  102.     references, keeping track of the path, etc.
  103.  
  104.     The subclasses of ConverterBase are responsible for creating a
  105.     convertObject method, and adding validation to the convertData method
  106.     (SavableConverter does validation at the begining, SavableUnconverter does
  107.     it at the end).
  108.     '''
  109.     
  110.     def __init__(self, objectSchemas = None):
  111.         '''Contruct a converter.  object schemas is a list of ObjectSchema
  112.         objects to use.  If none is given (the default), the schemas will be
  113.         taken from schema.objectSchemas.
  114.         '''
  115.         if objectSchemas is None:
  116.             objectSchemas = schema_mod.objectSchemas
  117.         
  118.         self.objectSchemaLookup = { }
  119.         self.classesToStrings = { }
  120.         self.stringsToClasses = { }
  121.         for os in objectSchemas:
  122.             self.stringsToClasses[os.classString] = os.klass
  123.             self.classesToStrings[os.klass] = os.classString
  124.             self.objectSchemaLookup[os.klass] = os
  125.         
  126.  
  127.     
  128.     def convertData(self, data, schema, path = ''):
  129.         '''Convert one piece of data.
  130.  
  131.         Arguments:
  132.             data -- piece of data to be converted
  133.             schema -- schema that the data should conform to
  134.             path -- string describing how we got to this object.  Its format
  135.                 is totally arbitrary, we just use it to help debug validation
  136.                 errors.
  137.         '''
  138.         
  139.         try:
  140.             self.preValidate(data, schema)
  141.         except schema_mod.ValidationError:
  142.             e = None
  143.             self.handleValidationError(e, data, path, schema)
  144.  
  145.         if data is None:
  146.             rv = None
  147.         elif isinstance(schema, schema_mod.SchemaSimpleItem):
  148.             rv = data
  149.         elif isinstance(schema, schema_mod.SchemaList):
  150.             rv = self.convertList(data, schema, path)
  151.         elif isinstance(schema, schema_mod.SchemaDict):
  152.             rv = self.convertDict(data, schema, path)
  153.         elif isinstance(schema, schema_mod.SchemaObject):
  154.             rv = self.convertObject(data, schema, path)
  155.         else:
  156.             raise ValueError('%s has an unknown SchemaItem type' % schema)
  157.         
  158.         try:
  159.             self.postValidate(rv, schema)
  160.         except schema_mod.ValidationError:
  161.             e = None
  162.             self.handleValidationError(e, data, path, schema)
  163.  
  164.         return rv
  165.  
  166.     
  167.     def convertList(self, list, schema, path):
  168.         childSchema = schema.childSchema
  169.         rv = []
  170.         for i in xrange(len(list)):
  171.             child = list[i]
  172.             newPath = path + '\n[%d] -> %s' % (i, util.stringify(child))
  173.             rv.append(self.convertData(child, childSchema, newPath))
  174.         
  175.         return rv
  176.  
  177.     
  178.     def convertDict(self, dict, schema, path):
  179.         keySchema = schema.keySchema
  180.         valueSchema = schema.valueSchema
  181.         rv = { }
  182.         for key, value in dict.items():
  183.             newPath = path + '\nkey: %s' % key
  184.             newKey = self.convertData(key, keySchema, newPath)
  185.             newPath = path + '\n{%s} -> %s' % (util.stringify(key), util.stringify(value))
  186.             newValue = self.convertData(value, valueSchema, newPath)
  187.             rv[newKey] = newValue
  188.         
  189.         return rv
  190.  
  191.     
  192.     def convertObjectList(self, objects):
  193.         '''Convert a list of objects.  This is the top-level method that the
  194.         saveDatabase and restoreDatabase methods use to convert a list of
  195.         DDBObjects to/from SavableObjects.
  196.         '''
  197.         retval = []
  198.         self.memory = { }
  199.         for object, schema in self.prepareObjectList(objects):
  200.             path = '%s' % object
  201.             retval.append(self.convertData(object, schema, path))
  202.         
  203.         self.onPostConversion()
  204.         return retval
  205.  
  206.     
  207.     def convertObject(self, object, schema, path):
  208.         if id(object) in self.memory:
  209.             return self.memory[id(object)]
  210.         
  211.         
  212.         try:
  213.             objectSchema = self.getObjectSchema(object)
  214.         except schema_mod.ValidationError:
  215.             e = None
  216.             self.handleValidationError(e, object, path, schema)
  217.  
  218.         convertedObject = self.makeNewConvert(objectSchema.classString)
  219.         self.memory[id(object)] = convertedObject
  220.         for name, schema in objectSchema.fields:
  221.             
  222.             try:
  223.                 data = self.getSourceAttr(object, name)
  224.             except schema_mod.ValidationError:
  225.                 e = None
  226.                 self.handleValidationError(e, object, path, schema)
  227.  
  228.             
  229.             try:
  230.                 dataStr = str(data)
  231.             except Exception:
  232.                 e = None
  233.                 dataStr = "<couldn't convert (%s)>" % e
  234.  
  235.             newPath = path + '\n%s -> %s' % (util.stringify(name), util.stringify(dataStr))
  236.             convertedData = self.convertData(data, schema, newPath)
  237.             self.setTargetAttr(convertedObject, name, convertedData)
  238.         
  239.         return convertedObject
  240.  
  241.     
  242.     def preValidate(self, data, schema):
  243.         '''Can be used to validate that a piece of data that is about to be
  244.         converted matches the schema for it.
  245.         '''
  246.         pass
  247.  
  248.     
  249.     def postValidate(self, converted, schema):
  250.         '''Can be used to validate that a converted piece of data matches the
  251.         schema for it.
  252.         '''
  253.         pass
  254.  
  255.     
  256.     def getSourceAttr(self, object, attrName):
  257.         '''Retrive the value of an attribute on a source object.'''
  258.         
  259.         try:
  260.             return getattr(object, attrName)
  261.         except AttributeError:
  262.             msg = "%s doesn't have the %s attribute" % (object, attrName)
  263.             raise schema_mod.ValidationError(msg)
  264.  
  265.  
  266.     
  267.     def setTargetAttr(self, object, attrName, attrValue):
  268.         '''Set the value of an attribute on a target object.'''
  269.         setattr(object, attrName, attrValue)
  270.  
  271.     
  272.     def handleValidationError(self, e, object, path, schema):
  273.         reason = e.args[0]
  274.         message = 'Error validating object %r\n\nPath:\n%s\n\nSchema: %s\nReason: %s' % (object, path, schema, reason)
  275.         raise schema_mod.ValidationError(message)
  276.  
  277.     
  278.     def onPostConversion(self):
  279.         '''Called when the conversion process is done, just before
  280.         we return the result.'''
  281.         pass
  282.  
  283.     
  284.     def getObjectSchema(self, object):
  285.         '''Get an ObjectSchema for a object to be converted.'''
  286.         raise NotImplementError()
  287.  
  288.     
  289.     def prepareObjectList(self, objectList):
  290.         '''Do the prep work for convertObjectList.
  291.  
  292.         Given a list of objects, return a list of (object, schema) tuples
  293.         that should be converted.
  294.         '''
  295.         raise NotImplementError()
  296.  
  297.     
  298.     def makeNewConvert(self, classString):
  299.         '''Construct a new object to use as our converted value.
  300.  
  301.         SavableConverter returns a SavableObject, SavableUnconverter returns a
  302.         DDBObject.
  303.         '''
  304.         raise NotImplementError()
  305.  
  306.  
  307.  
  308. class SavableConverter(ConverterBase):
  309.     '''Used to convert a list of DDBObjects into a list with the same
  310.     structure, but with DDBObject converted to SavableObjects.
  311.     '''
  312.     
  313.     def prepareObjectList(self, objectList):
  314.         rv = []
  315.         for object in objectList:
  316.             if object.__class__ in self.classesToStrings:
  317.                 rv.append((object, schema_mod.SchemaObject(object.__class__)))
  318.                 continue
  319.         
  320.         return rv
  321.  
  322.     
  323.     def getObjectSchema(self, object):
  324.         
  325.         try:
  326.             return self.objectSchemaLookup[object.__class__]
  327.         except KeyError:
  328.             msg = 'No ObjectSchema for %s' % object.__class__
  329.             raise schema_mod.ValidationError(msg)
  330.  
  331.  
  332.     
  333.     def preValidate(self, data, schema):
  334.         schema.validate(data)
  335.  
  336.     
  337.     def makeNewConvert(self, classString):
  338.         return SavableObject(classString)
  339.  
  340.     
  341.     def setTargetAttr(self, savable, attrName, attrValue):
  342.         savable.savedData[attrName] = attrValue
  343.  
  344.  
  345.  
  346. class SavableUnconverter(ConverterBase):
  347.     '''Used to reverse the work of SavableConverter.'''
  348.     
  349.     def prepareObjectList(self, objectList):
  350.         rv = []
  351.         for o in objectList:
  352.             klass = self.stringsToClasses[o.classString]
  353.             rv.append((o, schema_mod.SchemaObject(klass)))
  354.         
  355.         return rv
  356.  
  357.     
  358.     def getObjectSchema(self, object):
  359.         klass = self.stringsToClasses[object.classString]
  360.         return self.objectSchemaLookup[klass]
  361.  
  362.     
  363.     def makeNewConvert(self, classString):
  364.         restored = _BootStrapClass()
  365.         restored.__class__ = self.stringsToClasses[classString]
  366.         return restored
  367.  
  368.     
  369.     def getSourceAttr(self, savable, attrName):
  370.         
  371.         try:
  372.             return savable.savedData[attrName]
  373.         except KeyError:
  374.             msg = "SavableObject: %s doesn't have %s " % (savable.classString, attrName)
  375.             raise schema_mod.ValidationError(msg)
  376.  
  377.  
  378.     
  379.     def postValidate(self, converted, schema):
  380.         schema.validate(converted)
  381.  
  382.     
  383.     def handleValidationError(self, e, object, path, schema):
  384.         reason = e.args[0]
  385.         message = 'Error validating object %r\nWill use data anyway, bad things may happen soon\n\nPath:\n%s\n\nSchema: %s\nReason: %s' % (object, path, schema, reason)
  386.         raise schema_mod.ValidationWarning(message)
  387.  
  388.     
  389.     def onPostConversion(self):
  390.         if not skipOnRestore:
  391.             for object in self.memory.values():
  392.                 if hasattr(object, 'onRestore'):
  393.                     object.onRestore()
  394.                     continue
  395.             
  396.         
  397.  
  398.  
  399.  
  400. def objectsToSavables(objects, objectSchemas = None):
  401.     '''Transform a list of objects into something that we can save to disk.
  402.     This means converting any DDBObjects into SavebleObjects.
  403.     '''
  404.     saver = SavableConverter(objectSchemas)
  405.     return saver.convertObjectList(objects)
  406.  
  407. oneSaver = SavableConverter()
  408. oneRestorer = SavableUnconverter()
  409.  
  410. def objectToSavable(object):
  411.     '''Transform a list of objects into something that we can save to disk.
  412.     This means converting any DDBObjects into SavebleObjects.
  413.     '''
  414.     if object.__class__ in oneSaver.classesToStrings:
  415.         oneSaver.memory = { }
  416.         return oneSaver.convertObject(object, schema_mod.SchemaObject(object.__class__), '')
  417.     else:
  418.         return None
  419.  
  420.  
  421. def savablesToObjects(savedObjects, objectSchemas = None):
  422.     '''Reverses the work of objectsToSavables'''
  423.     restorer = SavableUnconverter(objectSchemas)
  424.     restorer.objectSchemas = objectSchemas
  425.     return restorer.convertObjectList(savedObjects)
  426.  
  427.  
  428. def savableToObject(savedObject):
  429.     '''Transform a list of objects into something that we can save to disk.
  430.     This means converting any DDBObjects into SavebleObjects.
  431.     '''
  432.     oneRestorer.memory = { }
  433.     klass = oneRestorer.stringsToClasses[savedObject.classString]
  434.     object = oneRestorer.convertObject(savedObject, schema_mod.SchemaObject(klass), '')
  435.     oneRestorer.onPostConversion()
  436.     return object
  437.  
  438.  
  439. def saveObjectList(objects, pathname, objectSchemas = None, version = None):
  440.     '''Save a list of objects to disk.'''
  441.     if version is None:
  442.         version = schema_mod.VERSION
  443.     
  444.     savableObjects = objectsToSavables(objects, objectSchemas)
  445.     toPickle = (version, savableObjects)
  446.     f = open(pathname, 'wb')
  447.     f.write(FILEMAGIC)
  448.     
  449.     try:
  450.         cPickle.dump(toPickle, f, cPickle.HIGHEST_PROTOCOL)
  451.     finally:
  452.         f.close()
  453.  
  454.  
  455.  
  456. def loadPickle(pathname, objectSchemas = None):
  457.     '''Restore a list of objects saved with saveObjectList.'''
  458.     f = open(pathname, 'rb')
  459.     
  460.     try:
  461.         if f.read(len(FILEMAGIC)) != FILEMAGIC:
  462.             msg = "%s doesn't seem to be a democracy database" % pathname
  463.             raise BadFileFormatError(pathname)
  464.         
  465.         (version, savedObjects) = cPickle.load(f)
  466.     finally:
  467.         f.close()
  468.  
  469.     if not skipUpgrade:
  470.         if version != schema_mod.VERSION:
  471.             shutil.copyfile(pathname, pathname + '.beforeupgrade')
  472.         
  473.         databaseupgrade.upgrade(savedObjects, version)
  474.     
  475.     return savablesToObjects(savedObjects, objectSchemas)
  476.  
  477.  
  478. def restoreObjectList(pathname, objectSchemas = None):
  479.     '''Restore a list of objects saved with saveObjectList.'''
  480.     return loadPickle(pathname, objectSchemas)
  481.  
  482.  
  483. def getObjects(pathname, convertOnFail):
  484.     '''Restore a database object.'''
  485.     global skipOnRestore, skipOnRestore
  486.     pathname = os.path.expanduser(pathname)
  487.     if not os.path.exists(pathname):
  488.         tempPathname = pathname + '.temp'
  489.         if os.path.exists(tempPathname):
  490.             os.rename(tempPathname, pathname)
  491.         else:
  492.             return None
  493.     
  494.     oldSkipOnRestore = skipOnRestore
  495.     skipOnRestore = True
  496.     
  497.     try:
  498.         objects = restoreObjectList(pathname)
  499.     except BadFileFormatError:
  500.         if convertOnFail:
  501.             logging.info('trying to convert database from old version')
  502.             import olddatabaseupgrade
  503.             olddatabaseupgrade.convertOldDatabase(pathname)
  504.             objects = restoreObjectList(pathname)
  505.             logging.info('*** Conversion Successfull ***')
  506.         else:
  507.             raise 
  508.     except ImportError:
  509.         e = None
  510.         if e.args == ('No module named storedatabase\r',):
  511.             logging.info('trying to convert text-mode database')
  512.             f = open(pathname, 'rt')
  513.             data = f.read()
  514.             f.close()
  515.             f = open(pathname, 'wb')
  516.             f.write(data.replace('\r\n', '\n'))
  517.             f.close()
  518.             objects = restoreObjectList(pathname)
  519.         else:
  520.             raise 
  521.     except:
  522.         e.args == ('No module named storedatabase\r',)
  523.  
  524.     import databasesanity
  525.     
  526.     try:
  527.         databasesanity.checkSanity(objects, quiet = True, reallyQuiet = not (util.chatter))
  528.     except databasesanity.DatabaseInsaneError:
  529.         e = None
  530.         util.failedExn('When restoring database', e)
  531.  
  532.     skipOnRestore = oldSkipOnRestore
  533.     if not skipOnRestore:
  534.         for object in objects:
  535.             if hasattr(object, 'onRestore'):
  536.                 object.onRestore()
  537.                 continue
  538.         
  539.     
  540.     return objects
  541.  
  542.  
  543. def restoreDatabase(db = None, pathname = None, convertOnFail = True):
  544.     if db is None:
  545.         db = database.defaultDatabase
  546.     
  547.     if pathname is None:
  548.         pathname = config.get(prefs.DB_PATHNAME)
  549.     
  550.     objects = getObjects(pathname, convertOnFail)
  551.     if objects:
  552.         db.restoreFromObjectList(objects)
  553.     
  554.  
  555. VERSION_KEY = 'Democracy Version'
  556.  
  557. class LiveStorageBDB:
  558.     TRANSACTION_TIMEOUT = 10
  559.     TRANSACTION_NAME = 'Save database'
  560.     
  561.     def __init__(self, dbPath = None, restore = True):
  562.         database.confirmDBThread()
  563.         
  564.         try:
  565.             self.txn = None
  566.             self.dc = None
  567.             self.toUpdate = set()
  568.             self.toRemove = set()
  569.             self.errorState = False
  570.             if dbPath is not None:
  571.                 self.dbPath = dbPath
  572.             else:
  573.                 self.dbPath = config.get(prefs.BSDDB_PATHNAME)
  574.             start = clock()
  575.             self.openEmptyDB()
  576.             if restore:
  577.                 
  578.                 try:
  579.                     
  580.                     try:
  581.                         self.db.open('database')
  582.                         self.version = int(self.db[VERSION_KEY])
  583.                     except (bsddb.db.DBNoSuchFileError, KeyError):
  584.                         self.closeInvalidDB()
  585.                         
  586.                         try:
  587.                             restoreDatabase()
  588.                         except KeyboardInterrupt:
  589.                             raise 
  590.                         except:
  591.                             logging.exception('Error restoring old database')
  592.  
  593.                         self.saveDatabase()
  594.  
  595.                     self.loadDatabase()
  596.                 except KeyboardInterrupt:
  597.                     raise 
  598.                 except databaseupgrade.DatabaseTooNewError:
  599.                     raise 
  600.                 except:
  601.                     None<EXCEPTION MATCH>KeyboardInterrupt
  602.                     self.handleDatabaseLoadError()
  603.                 
  604.  
  605.             None<EXCEPTION MATCH>KeyboardInterrupt
  606.             self.saveDatabase()
  607.             end = clock()
  608.             if end - start > 0.05 and util.chatter:
  609.                 logging.timing('Database load slow: %.3f', end - start)
  610.         except bsddb.db.DBNoSpaceError:
  611.             frontend.exit(28)
  612.  
  613.  
  614.     
  615.     def dumpDatabase(self, db):
  616.         global indentation, indentation, memory, indentation
  617.         nextFreeFilename = nextFreeFilename
  618.         import download_utils
  619.         output = open(nextFreeFilename(os.path.join(config.get(prefs.SUPPORT_DIRECTORY), 'database-dump.xml')), 'w')
  620.         indentation = 0
  621.         
  622.         def indent():
  623.             output.write('    ' * indentation)
  624.  
  625.         
  626.         def output_object(o):
  627.             global indentation, indentation, indentation, indentation
  628.             indent()
  629.             if o in memory:
  630.                 if o.savedData.has_key('id'):
  631.                     output.write('<%s id="%s"/>\n' % (o.classString, o.savedData['id']))
  632.                 else:
  633.                     output.write('<%s/>\n' % (o.classString,))
  634.                 return None
  635.             
  636.             memory.add(o)
  637.             if o.savedData.has_key('id'):
  638.                 output.write('<%s id="%s">\n' % (o.classString, o.savedData['id']))
  639.             else:
  640.                 output.write('<%s>\n' % (o.classString,))
  641.             indentation = indentation + 1
  642.             for key in o.savedData:
  643.                 if key == 'id':
  644.                     continue
  645.                 
  646.                 indent()
  647.                 output.write('<%s>' % (key,))
  648.                 value = o.savedData[key]
  649.                 if isinstance(value, SavableObject):
  650.                     output.write('\n')
  651.                     indentation = indentation + 1
  652.                     output_object(value)
  653.                     indentation = indentation - 1
  654.                     indent()
  655.                 else:
  656.                     output.write(str(value))
  657.                 output.write('</%s>\n' % (key,))
  658.             
  659.             indentation = indentation - 1
  660.             indent()
  661.             output.write('</%s>\n' % (o.classString,))
  662.  
  663.         output.write('<?xml version="1.0"?>\n')
  664.         output.write('<database schema="%d">\n' % (schema_mod.VERSION,))
  665.         indentation = indentation + 1
  666.         for o in db:
  667.             memory = set()
  668.             o = objectToSavable(o)
  669.             if o is not None:
  670.                 output_object(o)
  671.                 continue
  672.             (None, None, (None,))
  673.         
  674.         indentation = indentation - 1
  675.         output.write('</database>\n')
  676.         output.close()
  677.  
  678.     
  679.     def handleDatabaseLoadError(self):
  680.         database.confirmDBThread()
  681.         logging.exception('exception while loading database')
  682.         self.closeInvalidDB()
  683.         self.dbenv.close()
  684.         self.saveInvalidDB()
  685.         self.openEmptyDB()
  686.         self.saveDatabase()
  687.  
  688.     
  689.     def saveInvalidDB(self):
  690.         dir = os.path.dirname(self.dbPath)
  691.         saveName = 'corrupt_database'
  692.         i = 0
  693.         while os.path.exists(os.path.join(dir, saveName)):
  694.             i += 1
  695.             saveName = 'corrupt_database.%d' % i
  696.         os.rename(self.dbPath, os.path.join(dir, saveName))
  697.  
  698.     
  699.     def openEmptyDB(self):
  700.         database.confirmDBThread()
  701.         
  702.         try:
  703.             os.makedirs(self.dbPath)
  704.         except KeyboardInterrupt:
  705.             raise 
  706.         except:
  707.             pass
  708.  
  709.         self.dbenv = bsddb.db.DBEnv()
  710.         self.dbenv.set_flags(bsddb.db.DB_AUTO_COMMIT | bsddb.db.DB_TXN_NOSYNC, True)
  711.         self.dbenv.set_lg_max(1048576)
  712.         self.dbenv.open(self.dbPath, bsddb.db.DB_INIT_LOG | bsddb.db.DB_INIT_MPOOL | bsddb.db.DB_INIT_TXN | bsddb.db.DB_RECOVER | bsddb.db.DB_CREATE)
  713.         self.db = bsddb.db.DB(self.dbenv)
  714.         self.closed = False
  715.  
  716.     
  717.     def closeInvalidDB(self):
  718.         database.confirmDBThread()
  719.         
  720.         try:
  721.             self.db.close()
  722.         except KeyboardInterrupt:
  723.             raise 
  724.         except:
  725.             pass
  726.  
  727.         self.db = None
  728.  
  729.     
  730.     def upgradeDatabase(self):
  731.         database.confirmDBThread()
  732.         logging.info('Upgrading database...')
  733.         savables = []
  734.         cursor = self.db.cursor()
  735.         while True:
  736.             next = cursor.next()
  737.             if next is None:
  738.                 break
  739.             
  740.             (key, data) = next
  741.             if key != VERSION_KEY:
  742.                 
  743.                 try:
  744.                     savable = cPickle.loads(data)
  745.                     savables.append(savable)
  746.                 except KeyboardInterrupt:
  747.                     raise 
  748.                 except:
  749.                     None<EXCEPTION MATCH>KeyboardInterrupt
  750.                     logging.info('Error loading data in upgradeDatabase')
  751.                     raise 
  752.                 
  753.  
  754.             None<EXCEPTION MATCH>KeyboardInterrupt
  755.         cursor.close()
  756.         changed = databaseupgrade.upgrade(savables, self.version)
  757.         txn = self.dbenv.txn_begin()
  758.         self.version = schema_mod.VERSION
  759.         self.db.put(VERSION_KEY, str(self.version), txn = txn)
  760.         txn.commit()
  761.         self.db.sync()
  762.         objects = savablesToObjects(savables)
  763.         db = database.defaultDatabase
  764.         db.restoreFromObjectList(objects)
  765.  
  766.     
  767.     def rewriteDatabase(self, savables, txn):
  768.         '''Delete, then rewrite the entire database.  savables is a list of
  769.         SavableObjects that will be in the new database.  WARNING: This method
  770.         will probably take a long time.
  771.         '''
  772.         database.confirmDBThread()
  773.         logging.info('Rewriting database')
  774.         cursor = self.db.cursor(txn = txn)
  775.         while True:
  776.             next = cursor.next()
  777.             if next is None:
  778.                 break
  779.             
  780.             cursor.delete()
  781.         cursor.close()
  782.         for o in savables:
  783.             data = cPickle.dumps(o, cPickle.HIGHEST_PROTOCOL)
  784.             self.db.put(str(o.savedData['id']), data, txn = txn)
  785.         
  786.  
  787.     
  788.     def loadDatabase(self):
  789.         database.confirmDBThread()
  790.         upgrade = self.version != schema_mod.VERSION
  791.         if upgrade:
  792.             return self.upgradeDatabase()
  793.         
  794.         objects = []
  795.         cursor = self.db.cursor()
  796.         while True:
  797.             next = cursor.next()
  798.             if next is None:
  799.                 break
  800.             
  801.             (key, data) = next
  802.             if key != VERSION_KEY:
  803.                 
  804.                 try:
  805.                     savable = cPickle.loads(data)
  806.                     object = savableToObject(savable)
  807.                     objects.append(object)
  808.                 except KeyboardInterrupt:
  809.                     raise 
  810.                 except:
  811.                     None<EXCEPTION MATCH>KeyboardInterrupt
  812.                     logging.info('Error loading data in loadDatabase')
  813.                     raise 
  814.                 
  815.  
  816.             None<EXCEPTION MATCH>KeyboardInterrupt
  817.         cursor.close()
  818.         db = database.defaultDatabase
  819.         db.restoreFromObjectList(objects)
  820.  
  821.     
  822.     def saveDatabase(self):
  823.         database.confirmDBThread()
  824.         db = database.defaultDatabase
  825.         self.txn = self.dbenv.txn_begin()
  826.         self.db = bsddb.db.DB(self.dbenv)
  827.         self.db.open('database', flags = bsddb.db.DB_CREATE, dbtype = bsddb.db.DB_HASH, txn = self.txn)
  828.         for o in db.objects:
  829.             self.update(o[0])
  830.         
  831.         self.version = schema_mod.VERSION
  832.         self.db.put(VERSION_KEY, str(self.version), txn = self.txn)
  833.         self.txn.commit()
  834.         self.txn = None
  835.         self.db.sync()
  836.  
  837.     
  838.     def sync(self):
  839.         database.confirmDBThread()
  840.         self.db.sync()
  841.  
  842.     
  843.     def close(self):
  844.         database.confirmDBThread()
  845.         self.runUpdate()
  846.         self.closed = True
  847.         self.db.close()
  848.         self.dbenv.close()
  849.  
  850.     
  851.     def runUpdate(self):
  852.         database.confirmDBThread()
  853.         
  854.         try:
  855.             self.txn = self.dbenv.txn_begin()
  856.             for object in self.toRemove:
  857.                 
  858.                 try:
  859.                     self.remove(object)
  860.                 continue
  861.                 except bsddb.db.DBNotFoundError:
  862.                     continue
  863.                 
  864.  
  865.             
  866.             for object in self.toUpdate:
  867.                 self.update(object)
  868.             
  869.             self.txn.commit()
  870.             self.sync()
  871.             self.txn = None
  872.             self.dc = None
  873.             self.toUpdate = set()
  874.             self.toRemove = set()
  875.             if self.errorState:
  876.                 title = _('%s database save succeeded') % (config.get(prefs.SHORT_APP_NAME),)
  877.                 description = _('The database has been successfully saved. It is now safe to quit without losing any data.')
  878.                 dialogs.MessageBoxDialog(title, description).run()
  879.                 self.errorState = False
  880.         except bsddb.db.DBNoSpaceError:
  881.             err = None
  882.             if not self.errorState:
  883.                 title = _('%s database save failed') % (config.get(prefs.SHORT_APP_NAME),)
  884.                 description = _('%s was unable to save its database: Disk Full.\nWe suggest deleting files from the full disk or simply deleting some movies from your collection.\nRecent changes may be lost.') % config.get(prefs.LONG_APP_NAME)
  885.                 dialogs.MessageBoxDialog(title, description).run()
  886.                 self.errorState = True
  887.             
  888.             
  889.             try:
  890.                 self.txn.abort()
  891.             except KeyboardInterrupt:
  892.                 raise 
  893.             except:
  894.                 pass
  895.  
  896.             self.txn = None
  897.             self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  898.  
  899.  
  900.     
  901.     def update(self, object):
  902.         database.confirmDBThread()
  903.         if self.closed:
  904.             return None
  905.         
  906.         if self.txn is None:
  907.             self.toUpdate.add(object)
  908.             if self.dc is None:
  909.                 self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  910.             
  911.         else:
  912.             savable = objectToSavable(object)
  913.             if savable:
  914.                 key = str(object.id)
  915.                 data = cPickle.dumps(savable, cPickle.HIGHEST_PROTOCOL)
  916.                 self.db.put(key, data, txn = self.txn)
  917.             
  918.  
  919.     
  920.     def remove(self, object):
  921.         database.confirmDBThread()
  922.         if self.closed:
  923.             return None
  924.         
  925.         if self.txn is None:
  926.             self.toRemove.add(object)
  927.             
  928.             try:
  929.                 self.toUpdate.remove(object)
  930.             except KeyboardInterrupt:
  931.                 raise 
  932.             except:
  933.                 pass
  934.  
  935.             if self.dc is None:
  936.                 self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  937.             
  938.         else:
  939.             self.db.delete(str(object.id), txn = self.txn)
  940.  
  941.     
  942.     def checkpoint(self):
  943.         database.confirmDBThread()
  944.         
  945.         try:
  946.             if self.closed:
  947.                 return None
  948.             
  949.             self.dbenv.txn_checkpoint()
  950.             self.sync()
  951.             for logfile in self.dbenv.log_archive(bsddb.db.DB_ARCH_ABS):
  952.                 
  953.                 try:
  954.                     os.remove(logfile)
  955.                 continue
  956.                 except KeyboardInterrupt:
  957.                     raise 
  958.                     continue
  959.                     continue
  960.                 
  961.  
  962.         except bsddb.db.DBNoSpaceError:
  963.             pass
  964.  
  965.         eventloop.addTimeout(60, self.checkpoint, 'Remove Unused Database Logs')
  966.  
  967.     
  968.     def backupDatabase(self):
  969.         pass
  970.  
  971.  
  972.  
  973. class LiveStorage:
  974.     TRANSACTION_TIMEOUT = 10
  975.     TRANSACTION_NAME = 'Save database'
  976.     
  977.     def __init__(self, dbPath = None, restore = True):
  978.         database.confirmDBThread()
  979.         
  980.         try:
  981.             self.toUpdate = set()
  982.             self.toRemove = set()
  983.             self.errorState = False
  984.             self.closed = True
  985.             self.updating = False
  986.             self.dc = None
  987.             if dbPath is not None:
  988.                 self.dbPath = dbPath
  989.             else:
  990.                 self.dbPath = config.get(prefs.SQLITE_PATHNAME)
  991.             start = clock()
  992.             SQLiteDBExists = os.access(self.dbPath, os.F_OK)
  993.             self.openDatabase()
  994.             if restore:
  995.                 
  996.                 try:
  997.                     if SQLiteDBExists:
  998.                         self.cursor.execute('SELECT serialized_value FROM dtv_variables WHERE name=:name', {
  999.                             'name': VERSION_KEY })
  1000.                         self.version = self.cursor.fetchone()
  1001.                         if self.version:
  1002.                             self.version = cPickle.loads(str(self.version[0]))
  1003.                         else:
  1004.                             self.version = schema_mod.VERSION
  1005.                         self.loadDatabase()
  1006.                     else:
  1007.                         self.version = None
  1008.                         if os.access(config.get(prefs.BSDDB_PATHNAME), os.F_OK) or os.access(config.get(prefs.DB_PATHNAME), os.F_OK):
  1009.                             logging.info('Upgrading from previous version of database')
  1010.                             
  1011.                             try:
  1012.                                 LiveStorageBDB()
  1013.                             logging.warning('Upgrading from previous version of database failed')
  1014.  
  1015.                         
  1016.                         self.saveDatabase()
  1017.                 except KeyboardInterrupt:
  1018.                     raise 
  1019.                 except databaseupgrade.DatabaseTooNewError:
  1020.                     raise 
  1021.                 except:
  1022.                     None<EXCEPTION MATCH>KeyboardInterrupt
  1023.                     self.handleDatabaseLoadError()
  1024.                 
  1025.  
  1026.             None<EXCEPTION MATCH>KeyboardInterrupt
  1027.             self.saveDatabase()
  1028.             eventloop.addIdle(self.checkpoint, 'Remove Unused Database Logs')
  1029.             end = clock()
  1030.             if end - start > 0.05 and util.chatter:
  1031.                 logging.timing('Database load slow: %.3f', end - start)
  1032.         except sql.DatabaseError:
  1033.             e = None
  1034.             logging.error(e)
  1035.             raise 
  1036.  
  1037.  
  1038.     
  1039.     def openDatabase(self):
  1040.         logging.info('Connecting to %s' % self.dbPath)
  1041.         
  1042.         try:
  1043.             os.makedirs(os.path.normpath(os.path.join(self.dbPath, os.path.pardir)))
  1044.         except:
  1045.             pass
  1046.  
  1047.         self.conn = sql.connect(self.dbPath, isolation_level = None)
  1048.         self.closed = False
  1049.         self.cursor = self.conn.cursor()
  1050.         self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
  1051.         tables = [ row[0] for row in self.cursor ]
  1052.         if 'dtv_variables' not in tables:
  1053.             self.cursor.execute('CREATE TABLE dtv_variables(\n            name TEXT PRIMARY KEY NOT NULL,\n            serialized_value BLOB NOT NULL\n    );')
  1054.         
  1055.  
  1056.     
  1057.     def dumpDatabase(self, db):
  1058.         global indentation, indentation, memory, indentation
  1059.         nextFreeFilename = nextFreeFilename
  1060.         import download_utils
  1061.         output = open(nextFreeFilename(os.path.join(config.get(prefs.SUPPORT_DIRECTORY), 'database-dump.xml')), 'w')
  1062.         indentation = 0
  1063.         
  1064.         def indent():
  1065.             output.write('    ' * indentation)
  1066.  
  1067.         
  1068.         def output_object(o):
  1069.             global indentation, indentation, indentation, indentation
  1070.             indent()
  1071.             if o in memory:
  1072.                 if o.savedData.has_key('id'):
  1073.                     output.write('<%s id="%s"/>\n' % (o.classString, o.savedData['id']))
  1074.                 else:
  1075.                     output.write('<%s/>\n' % (o.classString,))
  1076.                 return None
  1077.             
  1078.             memory.add(o)
  1079.             if o.savedData.has_key('id'):
  1080.                 output.write('<%s id="%s">\n' % (o.classString, o.savedData['id']))
  1081.             else:
  1082.                 output.write('<%s>\n' % (o.classString,))
  1083.             indentation = indentation + 1
  1084.             for key in o.savedData:
  1085.                 if key == 'id':
  1086.                     continue
  1087.                 
  1088.                 indent()
  1089.                 output.write('<%s>' % (key,))
  1090.                 value = o.savedData[key]
  1091.                 if isinstance(value, SavableObject):
  1092.                     output.write('\n')
  1093.                     indentation = indentation + 1
  1094.                     output_object(value)
  1095.                     indentation = indentation - 1
  1096.                     indent()
  1097.                 else:
  1098.                     output.write(str(value))
  1099.                 output.write('</%s>\n' % (key,))
  1100.             
  1101.             indentation = indentation - 1
  1102.             indent()
  1103.             output.write('</%s>\n' % (o.classString,))
  1104.  
  1105.         output.write('<?xml version="1.0"?>\n')
  1106.         output.write('<database schema="%d">\n' % (schema_mod.VERSION,))
  1107.         indentation = indentation + 1
  1108.         for o in db:
  1109.             memory = set()
  1110.             o = objectToSavable(o)
  1111.             if o is not None:
  1112.                 output_object(o)
  1113.                 continue
  1114.             (None, None, (None,))
  1115.         
  1116.         indentation = indentation - 1
  1117.         output.write('</database>\n')
  1118.         output.close()
  1119.  
  1120.     
  1121.     def backupDatabase(self):
  1122.         logging.info('Attempting to back up database')
  1123.         if not self.closed:
  1124.             
  1125.             try:
  1126.                 self.conn.close()
  1127.             traceback.print_exc()
  1128.  
  1129.         
  1130.         
  1131.         try:
  1132.             tempfilename = os.path.join(tempfile.gettempdir(), '%012ddatabasebackup.zip' % randrange(0, 0xE8D4A50FFFL))
  1133.             zipfile = ZipFile(tempfilename, 'w')
  1134.             for root, dirs, files in os.walk(config.get(prefs.SUPPORT_DIRECTORY)):
  1135.                 if os.path.normpath(root) != os.path.normpath(config.get(prefs.ICON_CACHE_DIRECTORY)) and not os.path.islink(root):
  1136.                     relativeroot = root[len(config.get(prefs.SUPPORT_DIRECTORY)):]
  1137.                     while len(relativeroot) > 0 and relativeroot[0] in ('/', '\\'):
  1138.                         relativeroot = relativeroot[1:]
  1139.                     for filen in files:
  1140.                         if not os.path.islink(os.path.join(root, filen)):
  1141.                             zipfile.write(os.path.join(root, filen), os.path.join(relativeroot, filen).encode('ascii', 'replace'))
  1142.                             continue
  1143.                     
  1144.             
  1145.             zipfile.close()
  1146.             logging.info('Database backed up to %s' % tempfilename)
  1147.             return tempfilename
  1148.         except:
  1149.             traceback.print_exc()
  1150.         finally:
  1151.             if not self.closed:
  1152.                 self.conn = sql.connect(self.dbPath, isolation_level = None)
  1153.                 self.cursor = self.conn.cursor()
  1154.             
  1155.  
  1156.  
  1157.     
  1158.     def handleDatabaseLoadError(self):
  1159.         database.confirmDBThread()
  1160.         logging.exception('exception while loading database')
  1161.         self.closeInvalidDB()
  1162.         self.saveInvalidDB()
  1163.         self.saveDatabase()
  1164.  
  1165.     
  1166.     def saveInvalidDB(self):
  1167.         dir = os.path.dirname(self.dbPath)
  1168.         saveName = 'corrupt_database'
  1169.         i = 0
  1170.         while os.path.exists(os.path.join(dir, saveName)):
  1171.             i += 1
  1172.             saveName = 'corrupt_database.%d' % i
  1173.         os.rename(self.dbPath, os.path.join(dir, saveName))
  1174.  
  1175.     
  1176.     def closeInvalidDB(self):
  1177.         database.confirmDBThread()
  1178.         self.conn.close()
  1179.         self.conn = None
  1180.  
  1181.     
  1182.     def upgradeDatabase(self):
  1183.         database.confirmDBThread()
  1184.         self.updating = True
  1185.         self.cursor.execute('BEGIN TRANSACTION')
  1186.         
  1187.         try:
  1188.             savables = []
  1189.             self.cursor.execute('SELECT id, serialized_object FROM dtv_objects')
  1190.             for next in self.cursor:
  1191.                 (key, data) = next
  1192.                 
  1193.                 try:
  1194.                     savable = cPickle.loads(str(data))
  1195.                     savables.append(savable)
  1196.                 continue
  1197.                 except KeyboardInterrupt:
  1198.                     raise 
  1199.                     continue
  1200.                     logging.info('Error loading data in upgradeDatabase')
  1201.                     raise 
  1202.                     continue
  1203.                 
  1204.  
  1205.             
  1206.             changed = databaseupgrade.upgrade(savables, self.version)
  1207.             if changed is None:
  1208.                 self.rewriteDatabase(savables)
  1209.             else:
  1210.                 savables_set = set()
  1211.                 for o in savables:
  1212.                     savables_set.add(o)
  1213.                 
  1214.                 for o in changed:
  1215.                     if o in savables_set:
  1216.                         data = cPickle.dumps(o, cPickle.HIGHEST_PROTOCOL)
  1217.                         self.cursor.execute('REPLACE INTO dtv_objects (id, serialized_object) VALUES (?,?)', (int(o.savedData['id']), buffer(data)))
  1218.                         continue
  1219.                     self.cursor.execute('DELETE FROM dtv_objects WHERE id=?', (int(o.savedData['id']),))
  1220.                 
  1221.             self.version = schema_mod.VERSION
  1222.             self.cursor.execute('REPLACE INTO dtv_variables (name, serialized_value) VALUES (?,?)', (VERSION_KEY, buffer(cPickle.dumps(self.version, cPickle.HIGHEST_PROTOCOL))))
  1223.             objects = savablesToObjects(savables)
  1224.             db = database.defaultDatabase
  1225.             db.restoreFromObjectList(objects)
  1226.         finally:
  1227.             self.updating = False
  1228.             self.cursor.execute('COMMIT')
  1229.  
  1230.  
  1231.     
  1232.     def rewriteDatabase(self, savables):
  1233.         '''Delete, then rewrite the entire database.  savables is a list of
  1234.         SavableObjects that will be in the new database.  WARNING: This method
  1235.         will probably take a long time.
  1236.         '''
  1237.         database.confirmDBThread()
  1238.         logging.info('Rewriting database')
  1239.         if not self.updating:
  1240.             self.cursor.execute('BEGIN TRANSACTION')
  1241.         
  1242.         
  1243.         try:
  1244.             self.cursor.execute('DELETE FROM dtv_objects')
  1245.             for o in savables:
  1246.                 data = cPickle.dumps(o, cPickle.HIGHEST_PROTOCOL)
  1247.                 self.cursor.execute('REPLACE INTO dtv_objects (id, serialized_object) VALUES (?,?)', (int(o.savedData['id']), buffer(data)))
  1248.         finally:
  1249.             if not self.updating:
  1250.                 self.cursor.execute('COMMIT')
  1251.             
  1252.  
  1253.  
  1254.     
  1255.     def loadDatabase(self):
  1256.         database.confirmDBThread()
  1257.         upgrade = self.version != schema_mod.VERSION
  1258.         if upgrade:
  1259.             return self.upgradeDatabase()
  1260.         
  1261.         objects = []
  1262.         self.cursor.execute('SELECT id, serialized_object FROM dtv_objects')
  1263.         while True:
  1264.             next = self.cursor.fetchone()
  1265.             if next is None:
  1266.                 break
  1267.             
  1268.             (key, data) = next
  1269.             
  1270.             try:
  1271.                 savable = cPickle.loads(str(data))
  1272.                 object = savableToObject(savable)
  1273.                 objects.append(object)
  1274.             continue
  1275.             except KeyboardInterrupt:
  1276.                 raise 
  1277.                 continue
  1278.                 logging.info('Error loading data in loadDatabase')
  1279.                 raise 
  1280.                 continue
  1281.             
  1282.  
  1283.             None<EXCEPTION MATCH>KeyboardInterrupt
  1284.         self.cursor.close()
  1285.         db = database.defaultDatabase
  1286.         db.restoreFromObjectList(objects)
  1287.  
  1288.     
  1289.     def saveDatabase(self):
  1290.         database.confirmDBThread()
  1291.         db = database.defaultDatabase
  1292.         self.updating = True
  1293.         self.cursor.execute('BEGIN TRANSACTION')
  1294.         
  1295.         try:
  1296.             self.cursor.execute('DELETE FROM dtv_objects WHERE 1=1')
  1297.             for o in db.objects:
  1298.                 self.update(o[0])
  1299.             
  1300.             self.version = schema_mod.VERSION
  1301.             self.cursor.execute('REPLACE INTO dtv_variables (name, serialized_value) VALUES (?,?)', (VERSION_KEY, buffer(cPickle.dumps(self.version, cPickle.HIGHEST_PROTOCOL))))
  1302.         finally:
  1303.             self.updating = False
  1304.             self.cursor.execute('COMMIT')
  1305.  
  1306.  
  1307.     
  1308.     def sync(self):
  1309.         database.confirmDBThread()
  1310.  
  1311.     
  1312.     def close(self):
  1313.         database.confirmDBThread()
  1314.         self.runUpdate()
  1315.         self.closed = True
  1316.         self.cursor.close()
  1317.         self.conn.close()
  1318.  
  1319.     
  1320.     def runUpdate(self):
  1321.         database.confirmDBThread()
  1322.         
  1323.         try:
  1324.             self.updating = True
  1325.             self.cursor.execute('BEGIN TRANSACTION')
  1326.             
  1327.             try:
  1328.                 for object in self.toRemove:
  1329.                     
  1330.                     try:
  1331.                         self.remove(object)
  1332.                     continue
  1333.                     except sql.DatabaseError:
  1334.                         e = None
  1335.                         continue
  1336.                     
  1337.  
  1338.                 
  1339.                 for object in self.toUpdate:
  1340.                     self.update(object)
  1341.             finally:
  1342.                 self.updating = False
  1343.                 self.cursor.execute('COMMIT')
  1344.  
  1345.             self.toUpdate = set()
  1346.             self.toRemove = set()
  1347.             if self.errorState:
  1348.                 title = _('%s database save succeeded') % (config.get(prefs.SHORT_APP_NAME),)
  1349.                 description = _('The database has been successfully saved. It is now safe to quit without losing any data.')
  1350.                 dialogs.MessageBoxDialog(title, description).run()
  1351.                 self.errorState = False
  1352.         except sql.DatabaseError:
  1353.             e = None
  1354.             print e
  1355.             if not self.errorState:
  1356.                 title = _('%s database save failed') % (config.get(prefs.SHORT_APP_NAME),)
  1357.                 description = _('%s was unable to save its database: Disk Full.\nWe suggest deleting files from the full disk or simply deleting some movies from your collection.\nRecent changes may be lost.') % config.get(prefs.LONG_APP_NAME)
  1358.                 dialogs.MessageBoxDialog(title, description).run()
  1359.                 self.errorState = True
  1360.                 self.updating = False
  1361.             
  1362.         except:
  1363.             self.errorState
  1364.  
  1365.         self.updating = False
  1366.         self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  1367.  
  1368.     
  1369.     def update(self, object):
  1370.         database.confirmDBThread()
  1371.         if self.closed:
  1372.             return None
  1373.         
  1374.         if not self.updating:
  1375.             self.toUpdate.add(object)
  1376.             if self.dc is None:
  1377.                 self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  1378.             
  1379.         else:
  1380.             savable = objectToSavable(object)
  1381.             if savable:
  1382.                 key = int(object.id)
  1383.                 data = cPickle.dumps(savable, cPickle.HIGHEST_PROTOCOL)
  1384.                 self.cursor.execute('REPLACE INTO dtv_objects (id, serialized_object) VALUES (?,?)', (int(key), buffer(data)))
  1385.             
  1386.  
  1387.     
  1388.     def remove(self, object):
  1389.         database.confirmDBThread()
  1390.         if self.closed:
  1391.             return None
  1392.         
  1393.         if not self.updating:
  1394.             self.toRemove.add(object)
  1395.             
  1396.             try:
  1397.                 self.toUpdate.remove(object)
  1398.             except KeyboardInterrupt:
  1399.                 raise 
  1400.             except:
  1401.                 pass
  1402.  
  1403.             if self.dc is None:
  1404.                 self.dc = eventloop.addTimeout(self.TRANSACTION_TIMEOUT, self.runUpdate, self.TRANSACTION_NAME)
  1405.             
  1406.         else:
  1407.             self.cursor.execute('DELETE FROM dtv_objects WHERE id=?', (int(object.id),))
  1408.  
  1409.     
  1410.     def checkpoint(self):
  1411.         database.confirmDBThread()
  1412.         eventloop.addTimeout(60, self.checkpoint, 'Remove Unused Database Logs')
  1413.  
  1414.  
  1415.